package com.lemon.play.goai.posjudge;

import java.util.HashSet;

public class GenInfluence implements Influencable {
	private int size = 19;
	private int edgeGap = 6;

	public GenInfluence(Desk desk) {
		this.desk = desk;
		size = Desk.SIZE;
		for (int i = 0; i < 19; i++) {
			for (int j = 0; j < 19; j++) {
				spots[i][j] = new ISpot();
			}
		}
	}

	public void begin() {
		size = Desk.SIZE;
		ebs.clear();
		init();
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				ISpot spot = spots[i][j];
				if (spot.getCamp() == null)
					continue;
				this.addPower(i, j, spot.getCamp());
			}
		}
		this.addEdgePower();
		addEyes();
	}

	private void addEdgePower() {
		for (int i = 0; i < edgeGap; i++) {
			for (int j = 0; j < size; j++) {
				Block block = spots[i][j].getBlock();
				if (block != null) {
					if (block.getCamp() == Spot.BLACK)
						block.addPowerWest(-INFLUENCE[i + 1]);
					else
						block.addPowerWest(INFLUENCE[i + 1]);
				}
				block = spots[size - 1 - i][j].getBlock();
				if (block != null) {
					if (block.getCamp() == Spot.BLACK)
						block.addPowerEast(-INFLUENCE[i + 1]);
					else
						block.addPowerEast(INFLUENCE[i + 1]);
				}
				block = spots[j][i].getBlock();
				if (block != null) {
					if (block.getCamp() == Spot.BLACK)
						block.addPowerNorth(-INFLUENCE[i + 1]);
					else
						block.addPowerNorth(INFLUENCE[i + 1]);
				}
				block = spots[j][size - 1 - i].getBlock();
				if (block != null) {
					if (block.getCamp() == Spot.BLACK)
						block.addPowerSouth(-INFLUENCE[i + 1]);
					else
						block.addPowerSouth(INFLUENCE[i + 1]);
				}
			}
		}
	}

	private void addPower(int x, int y, boolean camp) {
		int[] POWER = new int[7], INFLUENCE = new int[7];
		if (camp == Spot.BLACK) {
			for (int i = 0; i < 7; i++) {
				POWER[i] = GenInfluence.POWER[i];
				INFLUENCE[i] = GenInfluence.INFLUENCE[i];
			}
		} else {
			for (int i = 0; i < 7; i++) {
				POWER[i] = -GenInfluence.POWER[i];
				INFLUENCE[i] = -GenInfluence.INFLUENCE[i];
			}
		}
		ISpot spot = spots[x][y];
		Block block = spot.getBlock();
		block.addPower(POWER[0]);
		int[] p1 = new int[] { 0, 0, 0, 0 }, p2 = new int[] { 0, 0, 0, 0 }, p3 = new int[] { 0, 0, 0, 0 }, p4 = new int[] { 0, 0, 0, 0 };
		if (x < edgeGap) {
			p1[0] = POWER[x];
			p1[1] = x + 1 > edgeGap ? 0 : POWER[x + 1];
			p1[2] = x + 2 > edgeGap ? 0 : POWER[x + 2];
			p1[3] = x + 3 > edgeGap ? 0 : POWER[x + 3];
		}
		if (x > size - edgeGap) {
			p2[0] = POWER[size - 1 - x];
			p2[1] = size - x > edgeGap ? 0 : POWER[size - x];
			p2[2] = size + 1 - x > edgeGap ? 0 : POWER[size + 1 - x];
			p2[3] = size + 2 - x > edgeGap ? 0 : POWER[size + 2 - x];
		}
		if (y < edgeGap) {
			p3[0] = POWER[y];
			p3[1] = y + 1 > edgeGap ? 0 : POWER[y + 1];
			p3[2] = y + 2 > edgeGap ? 0 : POWER[y + 2];
			p3[3] = y + 3 > edgeGap ? 0 : POWER[y + 3];
		}
		if (y > size - edgeGap) {
			p4[0] = POWER[size - 1 - y];
			p4[1] = size - y > edgeGap ? 0 : POWER[size - y];
			p4[2] = size + 1 - y > edgeGap ? 0 : POWER[size + 1 - y];
			p4[3] = size + 2 - y > edgeGap ? 0 : POWER[size + 2 - y];
		}
		boolean h1 = true, h2 = true, h3 = true, h4 = true;
		int ix, iy;
		for (int length = 1; length < 7; length++) {
			// X+
			if (x + length < size) {
				Block block2 = spots[x + length][y].getBlock();
				if (h1)
					if (block2 == null) {
						spots[x + length][y].addPower(POWER[length] + p2[0]);
					} else {
						block2.addPowerWest(INFLUENCE[length]);
						if (block2 != block)
							h1 = false;
					}
			}
			// X-
			if (x - length >= 0) {
				Block block2 = spots[x - length][y].getBlock();
				if (h2)
					if (block2 == null) {
						spots[x - length][y].addPower(POWER[length] + p1[0]);
					} else {
						block2.addPowerEast(INFLUENCE[length]);
						if (block2 != block)
							h2 = false;
					}
			}
			// Y+
			if (y + length < size) {
				Block block2 = spots[x][y + length].getBlock();
				if (h3)
					if (block2 == null) {
						spots[x][y + length].addPower(POWER[length] + p4[0]);
					} else {
						block2.addPowerNorth(INFLUENCE[length]);
						if (block2 != block)
							h3 = false;
					}
			}
			// Y-
			if (y - length >= 0) {
				Block block2 = spots[x][y - length].getBlock();
				if (h4)
					if (block2 == null) {
						spots[x][y - length].addPower(POWER[length] + p3[0]);
					} else {
						block2.addPowerSouth(INFLUENCE[length]);
						if (block2 != block)
							h4 = false;
					}
			}
			for (ix = 1; ix < length; ix++) {
				iy = length - ix;
				int x1 = x + ix, y1 = y + iy;
				int x2 = x - ix, y2 = y - iy;
				boolean f1 = false, f2 = false, f3 = false, f4 = false, f5 = false, f6 = false, f7 = false, f8 = false;
				for (int i = 0; i <= ix; i++) {
					for (int j = 0; j <= iy; j++) {
						Boolean camp2;
						if (x1 < size && y1 < size) {// I
							camp2 = spots[x + i][y + j].getCamp();
							if (camp2 != null && camp != camp2) {
								if (i != 0 && j != iy) {
									f1 = true;// north
								}
								if (j != 0 && i != ix) {
									f2 = true;// west
								}
							}
						}
						if (x2 >= 0 && y1 < size) {// II
							camp2 = spots[x - i][y + j].getCamp();
							if (camp2 != null && camp != camp2) {
								if (i != 0 && j != iy) {
									f3 = true;// north
								}
								if (j != 0 && i != ix) {
									f4 = true;// east
								}
							}
						}
						if (x2 >= 0 && y2 >= 0) {// III
							camp2 = spots[x - i][y - j].getCamp();
							if (camp2 != null && camp != camp2) {
								if (i != 0 && j != iy) {
									f5 = true;// south
								}
								if (j != 0 && i != ix) {
									f6 = true;// east
								}
							}
						}
						if (x1 < size && y2 >= 0) {// IV
							camp2 = spots[x + i][y - j].getCamp();
							if (camp2 != null && camp != camp2) {
								if (i != 0 && j != iy)
									f7 = true;// north
								if (j != 0 && i != ix)
									f8 = true;// west
							}
						}
					}
				}
				if (x1 < size) {
					// I(x>0,y>0)
					if (y1 < size) {
						ISpot spot2 = spots[x1][y1];
						Block block2 = spot2.getBlock();
						if (block2 == null) {
							if (!f1 || !f2) {
								spot2.addPower(POWER[length]);
								if (ix == 1 && iy >= 1)
									spot2.addPower(p4[1]);
								else if (ix == 2 && iy >= 2)
									spot2.addPower(p4[2]);
								else if (ix == 3 && iy >= 3)
									spot2.addPower(p4[3]);
								if (iy == 1 && ix >= 1)
									spot2.addPower(p2[1]);
								else if (iy == 2 && ix >= 2)
									spot2.addPower(p2[2]);
								else if (iy == 3 && ix >= 3)
									spot2.addPower(p2[3]);
							}
						} else if (block2 != block) {
							double l = Math.sqrt(ix * ix + iy * iy);
							double p = INFLUENCE[0] * Math.pow(MODULUS, l);
							if (!f1) {
								int north = (int) (p * iy / l);
								block2.addPowerNorth(north);
							}
							if (!f2) {
								int west = (int) (p * ix / l);
								block2.addPowerWest(west);
							}
						}
					}
					// IV(x>0,y<0)
					if (y2 >= 0) {
						ISpot spot2 = spots[x1][y2];
						Block block2 = spot2.getBlock();
						if (block2 == null) {
							if (!f7 || !f8) {
								spot2.addPower(POWER[length]);
								if (ix == 1 && iy >= 1)
									spot2.addPower(p3[1]);
								else if (ix == 2 && iy >= 2)
									spot2.addPower(p3[2]);
								else if (ix == 3 && iy >= 3)
									spot2.addPower(p3[3]);
								if (iy == 1 && ix >= 1)
									spot2.addPower(p2[1]);
								else if (iy == 2 && ix >= 2)
									spot2.addPower(p2[2]);
								else if (iy == 3 && ix >= 3)
									spot2.addPower(p2[3]);
							}
						} else if (block2 != block) {
							double l = Math.sqrt(ix * ix + iy * iy);
							double p = INFLUENCE[0] * Math.pow(MODULUS, l);
							if (!f7) {
								int south = (int) (p * iy / l);
								block2.addPowerSouth(south);
							}
							if (!f8) {
								int west = (int) (p * ix / l);
								block2.addPowerWest(west);
							}
						}
					}
				}
				if (x2 >= 0) {
					// II(x<0,y>0)
					if (y1 < size) {
						ISpot spot2 = spots[x2][y1];
						Block block2 = spot2.getBlock();
						if (block2 == null) {
							if (!f3 || !f4) {
								spot2.addPower(POWER[length]);
								if (ix == 1 && iy >= 1)
									spot2.addPower(p4[1]);
								else if (ix == 2 && iy >= 2)
									spot2.addPower(p4[2]);
								else if (ix == 3 && iy >= 3)
									spot2.addPower(p4[3]);
								if (iy == 1 && ix >= 1)
									spot2.addPower(p1[1]);
								else if (iy == 2 && ix >= 2)
									spot2.addPower(p1[2]);
								else if (iy == 3 && ix >= 3)
									spot2.addPower(p1[3]);
							}
						} else if (block2 != block) {
							double l = Math.sqrt(ix * ix + iy * iy);
							double p = INFLUENCE[0] * Math.pow(MODULUS, l);
							if (!f3) {
								int north = (int) (p * iy / l);
								block2.addPowerNorth(north);
							}
							if (!f4) {
								int east = (int) (p * ix / l);
								block2.addPowerEast(east);
							}
						}
					}
					// III(x<0,y<0)
					if (y2 >= 0) {
						ISpot spot2 = spots[x2][y2];
						Block block2 = spot2.getBlock();
						if (block2 == null) {
							if (!f5 || !f6) {
								spot2.addPower(POWER[length]);
								if (ix == 1 && iy >= 1)
									spot2.addPower(p3[1]);
								else if (ix == 2 && iy >= 2)
									spot2.addPower(p3[2]);
								else if (ix == 3 && iy >= 3)
									spot2.addPower(p3[3]);
								if (iy == 1 && ix >= 1)
									spot2.addPower(p1[1]);
								else if (iy == 2 && ix >= 2)
									spot2.addPower(p1[2]);
								else if (iy == 3 && ix >= 3)
									spot2.addPower(p1[3]);
							}
						} else if (block2 != block) {
							double l = Math.sqrt(ix * ix + iy * iy);
							double p = INFLUENCE[0] * Math.pow(MODULUS, l);
							if (!f5) {
								int south = (int) (p * iy / l);
								block2.addPowerSouth(south);
							}
							if (!f6) {
								int east = (int) (p * ix / l);
								block2.addPowerEast(east);
							}
						}
					}
				}
			}
		}
	}

	private void init() {
		size = Desk.SIZE;
		switch (size) {
		case 9:
			edgeGap = 4;
			break;
		case 13:
			edgeGap = 5;
			break;
		case 19:
			edgeGap = 6;
			break;
		}
		Spot[][] source = desk.getDesk();
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				spots[i][j].setSpot(source[i][j]);
				spots[i][j].setPower(0);
				spots[i][j].setBlock(null);
				spots[i][j].setEmptyBlock(null);
				if (spots[i][j].getCamp() == Spot.NONE) {
					continue;
				}
				boolean sameLeft = false, sameUp = false;
				ISpot left = null, up = null;
				if (i > 0) {
					left = spots[i - 1][j];
					if (left.getCamp() == spots[i][j].getCamp()) {
						sameLeft = true;
					}
				}
				if (j > 0) {
					up = spots[i][j - 1];
					if (up.getCamp() == spots[i][j].getCamp()) {
						sameUp = true;
					}
				}
				if (sameLeft && sameUp) {
					Block block = left.getBlock();
					if (block != up.getBlock())
						block.addBlock(up.getBlock());
					block.addSpot(spots[i][j]);
				} else if (sameLeft) {
					Block block = left.getBlock();
					block.addSpot(spots[i][j]);
				} else if (sameUp) {
					Block block = up.getBlock();
					block.addSpot(spots[i][j]);
				} else {
					new Block(spots[i][j]);
				}
			}
		}
		addBlock();
		addEmptyBlock();
	}

	private void addBlock() {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				if (spots[i][j].getCamp() == Spot.NONE)
					continue;
				boolean sameUL = false, sameDL = false, sameDR = false, sameUR = false;
				boolean nullLeft = false, nullUp = false, nullDown = false, nullRight = false;
				ISpot left = null, up = null, ul = null, down = null, dl = null, right = null, dr = null, ur = null;
				if (i > 0) {
					left = spots[i - 1][j];
					if (left.getCamp() == Spot.NONE)
						nullLeft = true;
					if (j > 0) {
						ul = spots[i - 1][j - 1];
						if (ul.getCamp() == spots[i][j].getCamp())
							sameUL = true;
					}
					if (j < size - 1) {
						dl = spots[i - 1][j + 1];
						if (dl.getCamp() == spots[i][j].getCamp())
							sameDL = true;
					}
				}
				if (j > 0) {
					up = spots[i][j - 1];
					if (up.getCamp() == Spot.NONE)
						nullUp = true;
				}
				if (i < size - 1) {
					right = spots[i + 1][j];
					if (right.getCamp() == Spot.NONE)
						nullRight = true;
					if (j > 0) {
						ur = spots[i + 1][j - 1];
						if (ur.getCamp() == spots[i][j].getCamp())
							sameUR = true;
					}
					if (j < size - 1) {
						dr = spots[i + 1][j + 1];
						if (dr.getCamp() == spots[i][j].getCamp())
							sameDR = true;
					}
				}
				if (j < size - 1) {
					down = spots[i][j + 1];
					if (down.getCamp() == Spot.NONE)
						nullDown = true;
				}
				Block block = spots[i][j].getBlock();
				int roadUL = 0, roadUR = 0, roadDL = 0, roadDR = 0;
				if (nullUp) {
					roadUL++;
					roadUR++;
				}
				if (nullLeft) {
					roadUL++;
					roadDL++;
				}
				if (nullDown) {
					roadDL++;
					roadDR++;
				}
				if (nullRight) {
					roadUR++;
					roadDR++;
				}
				if (sameUL) {
					if (roadUL == 2) {
						Block b = spots[i - 1][j - 1].getBlock();
						if (b != block)
							b.addBlock(block);
					} else if (sameDR && roadUL == 1 && roadDR == 1) {
						Block b1 = spots[i - 1][j - 1].getBlock();
						Block b2 = spots[i + 1][j + 1].getBlock();
						if (b1 == b2 && b1 != block) {
							b1.addBlock(block);
						}
					}
					if (sameUR && nullUp) {
						Block b1 = spots[i - 1][j - 1].getBlock();
						Block b2 = spots[i + 1][j - 1].getBlock();
						if (b1 != block) {
							b1.addBlock(block);
						}
						if (b2 != block && b1 != b2) {
							b1.addBlock(b2);
						}
					}
					// if (sameDL && nullLeft && (roadUL==1 || roadDL==1)) {
					// Block b1 = spots[i - 1][j - 1].getBlock();
					// Block b2 = spots[i - 1][j + 1].getBlock();
					// if (b1 != block) {
					// b1.addBlock(block);
					// }
					// if (b2 != block && b1 != b2) {
					// b1.addBlock(b2);
					// }
					// }
				}
				if (sameDL) {
					if (roadDL == 2) {
						Block b = spots[i - 1][j + 1].getBlock();
						if (b != block)
							b.addBlock(block);
					} else if (sameUR && roadUR == 1 && roadDL == 1) {
						Block b1 = spots[i - 1][j + 1].getBlock();
						Block b2 = spots[i + 1][j - 1].getBlock();
						if (b1 == b2 && b1 != block) {
							b1.addBlock(block);
						}
					}
				}
				if (sameDR) {
					if (roadDR == 2) {
						Block b = spots[i + 1][j + 1].getBlock();
						if (b != block)
							b.addBlock(block);
					}
					// if (sameDL && nullDown) {
					// Block b1 = spots[i - 1][j + 1].getBlock();
					// Block b2 = spots[i + 1][j + 1].getBlock();
					// if (b1 != block) {
					// b1.addBlock(block);
					// }
					// if (b2 != block && b1 != b2) {
					// b1.addBlock(b2);
					// }
					// }
					// if (sameUR && nullRight) {
					// Block b1 = spots[i + 1][j - 1].getBlock();
					// Block b2 = spots[i + 1][j + 1].getBlock();
					// if (b1 != block) {
					// b1.addBlock(block);
					// }
					// if (b2 != block && b1 != b2) {
					// b1.addBlock(b2);
					// }
					// }
				}
				if (sameUR && roadUR == 2) {
					Block b = spots[i + 1][j - 1].getBlock();
					if (b != block)
						b.addBlock(block);
				}
			}
		}
	}

	private void addEmptyBlock() {
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				ISpot spot = spots[i][j];
				Block block = spot.getBlock();
				EmptyBlock eb = new EmptyBlock(spot);
				ebs.add(eb);
				if (block == null) {
					if (i > 0) {// up
						ISpot s = spots[i - 1][j];
						Block b = s.getBlock();
						if (b == null) {
							s.getEmptyBlock().addEmptyBlock(eb);
							ebs.remove(eb);
							eb = s.getEmptyBlock();
						} else {
							eb.addOver(b);
							if (s.getCamp() == Spot.BLACK)
								eb.setBlack();
							else
								eb.setWhite();
						}
					}
					if (i < size - 1) {// down
						ISpot s = spots[i + 1][j];
						Block b = s.getBlock();
						if (b != null) {
							eb.addOver(b);
							if (s.getCamp() == Spot.BLACK)
								eb.setBlack();
							else
								eb.setWhite();
						}
					}
					if (j > 0) {// left
						ISpot s = spots[i][j - 1];
						Block b = s.getBlock();
						if (b == null) {
							s.getEmptyBlock().addEmptyBlock(eb);
							ebs.remove(eb);
							eb = s.getEmptyBlock();
						} else {
							eb.addOver(b);
							if (s.getCamp() == Spot.BLACK)
								eb.setBlack();
							else
								eb.setWhite();
						}
					}
					if (j < size - 1) {// right
						ISpot s = spots[i][j + 1];
						Block b = s.getBlock();
						if (b != null) {
							eb.addOver(b);
							if (s.getCamp() == Spot.BLACK)
								eb.setBlack();
							else
								eb.setWhite();
						}
					}
				}
			}
		}
	}

	private void addEyes() {
		for (EmptyBlock block : ebs) {
			if (block.hasOnlyBlack() && block.getSize() < 30) {
				for (Block b : block.getOver())
					b.addEye(block);
			} else if (block.hasOnlyWhite() && block.getSize() < 30) {
				for (Block b : block.getOver())
					b.addEye(block);
			} else {
				boolean allBlack = true;
				boolean allWhite = true;
				for (ISpot spot : block.getSpots()) {
					if (spot.getPower() < MAX) {
						allBlack = false;
					} else if (spot.getPower() > -MAX) {
						allWhite = false;
					}
				}
				if (allBlack && block.getSize() < 30) {
					for (Block b : block.getOver()) {
						if (b.getCamp() == Spot.BLACK)
							b.addEye(block);
					}
				}
				if (allWhite && block.getSize() < 30) {
					for (Block b : block.getOver()) {
						if (b.getCamp() == Spot.WHITE)
							b.addEye(block);
					}
				}
			}
		}
	}

	@Override
	public int[][] getInfluence(Desk d) {
		begin();
		return getInfluence1();
	}

	private int[][] getInfluence1() {
		int[][] result = new int[size][size];
		for (int i = 0; i < size; i++)
			for (int j = 0; j < size; j++) {
				Block block = spots[i][j].getBlock();
				int power = 0;
				if (block != null) {
					if (block.isUndead()) {
						if (block.getCamp() == Spot.BLACK)
							result[i][j] = FREE;
						else
							result[i][j] = FREE;
						continue;
					}
					power = block.getPower();
					int pn = block.getPowerNorth();
					int pe = block.getPowerEast();
					int pw = block.getPowerWest();
					int ps = block.getPowerSouth();
					boolean camp = block.getCamp();
					if (camp == Spot.WHITE) {
						int c = 0;
						if (pn + power >= MAX)
							c++;
						if (pe + power >= MAX)
							c++;
						if (pw + power >= MAX)
							c++;
						if (ps + power >= MAX)
							c++;
						if (c > 2)
							result[i][j] = BLACK_INFLUENCE;
						else
							result[i][j] = FREE;
					} else if (camp == Spot.BLACK) {
						int c = 0;
						if (pn + power <= -MAX)
							c++;
						if (pe + power <= -MAX)
							c++;
						if (pw + power <= -MAX)
							c++;
						if (ps + power <= -MAX)
							c++;
						if (c > 2)
							result[i][j] = WHITE_INFLUENCE;
						else
							result[i][j] = FREE;
					}
				} else {
					power = spots[i][j].getPower();
					if (power >= MAX)
						result[i][j] = BLACK_INFLUENCE;
					else if (power <= -MAX)
						result[i][j] = WHITE_INFLUENCE;
					else
						result[i][j] = FREE;
				}
			}
		return result;
	}

	private Desk desk;
	private ISpot[][] spots = new ISpot[19][19];
	private HashSet<EmptyBlock> ebs = new HashSet<EmptyBlock>();

	private static final int[] POWER = new int[] { 1000, 600, 360, 215, 129, 77, 46 };
	private static final int[] INFLUENCE = new int[] { 2284, 1370, 822, 493, 295, 177, 106 };
	private static final int MAX = 370;
	private static final double MODULUS = 0.6;

	public static class ISpot extends Spot {
		public ISpot() {
			this.power = 0;
		}

		public ISpot(Spot spot) {
			super(spot.getX(), spot.getY(), spot.getCamp(), spot.getOrder());
			this.power = 0;
		}

		public void setSpot(Spot spot) {
			super.setX(spot.getX());
			super.setY(spot.getY());
			super.setOrder(spot.getOrder());
			super.setCamp(spot.getCamp());
		}

		public void setPower(int newPower) {
			this.power = newPower;
		}

		public int getPower() {
			return this.power;
		}

		public void addPower(int power) {
			this.power += power;
		}

		public void setBlock(Block block) {
			this.block = block;
		}

		public Block getBlock() {
			return block;
		}

		public EmptyBlock getEmptyBlock() {
			return this.eBlock;
		}

		public void setEmptyBlock(EmptyBlock eb) {
			this.eBlock = eb;
		}

		private int power = 0;
		private Block block;
		private EmptyBlock eBlock;
	}

	public static class Block {

		public Block() {
		}

		public Block(Boolean camp) {
			this.camp = camp;
		}

		public Block(ISpot first) {
			this.camp = first.getCamp();
			this.addSpot(first);
		}

		public boolean addSpot(ISpot spot) {
			if (spot == null)
				throw new NullPointerException("Param must be non-null");
			if (spot.getCamp() != this.camp)
				return false;
			if (spots.contains(spot)) {
				return false;
			}
			spots.add(spot);
			spot.setBlock(this);
			return true;
		}

		public boolean addBlock(Block block) {
			if (block == null) {
				throw new NullPointerException("Param must be non-null");
			}
			if (block.getCamp() != camp)
				return false;
			HashSet<ISpot> spots = block.getSpots();
			for (ISpot spot : spots) {
				if (this.spots.contains(spot)) {
					return false;
				}
			}
			for (ISpot spot : spots) {
				this.spots.add(spot);
				spot.setBlock(this);
			}
			this.power += block.power;
			this.powerEast += block.powerEast;
			this.powerNorth += block.powerNorth;
			this.powerSouth += block.powerSouth;
			this.powerWest += block.powerWest;
			return true;
		}

		public HashSet<ISpot> getSpots() {
			return spots;
		}

		public void setCamp(Boolean camp) {
			this.camp = camp;
		}

		public Boolean getCamp() {
			return camp;
		}

		public boolean contains(ISpot spot) {
			return spots.contains(spot);
		}

		public void setPower(int newPower) {
			this.power = newPower;
		}

		public int getPower() {
			return this.power;
		}

		public void addPower(int power) {
			this.power += power;
		}

		public int getPowerNorth() {
			return powerNorth;
		}

		public void setPowerNorth(int powerNorth) {
			this.powerNorth = powerNorth;
		}

		public int getPowerEast() {
			return powerEast;
		}

		public void setPowerEast(int powerEast) {
			this.powerEast = powerEast;
		}

		public int getPowerSouth() {
			return powerSouth;
		}

		public void setPowerSouth(int powerSouth) {
			this.powerSouth = powerSouth;
		}

		public int getPowerWest() {
			return powerWest;
		}

		public void setPowerWest(int powerWest) {
			this.powerWest = powerWest;
		}

		public void addPowerNorth(int north) {
			this.powerNorth += north;
		}

		public void addPowerEast(int east) {
			this.powerEast += east;
		}

		public void addPowerSouth(int south) {
			this.powerSouth += south;
		}

		public void addPowerWest(int west) {
			this.powerWest += west;
		}

		public void setLive(int live) {
			this.live = live;
		}

		public boolean isDying() {
			if (this.live == DYING)
				return true;
			return false;
		}

		public boolean isDead() {
			if (this.live == DEAD)
				return true;
			return false;
		}

		public boolean isAlive() {
			if (this.live == ALIVE)
				return true;
			return false;
		}

		public void addEye(EmptyBlock eye) {
			this.eyes.add(eye);
			if (eye.getSize() >= MIN_DOUBLE_EYES)
				eyeCount += 2;
			else
				eyeCount++;
		}

		public boolean isUndead() {
			if (eyeCount > 1)
				return true;
			return false;
		}

		private int powerNorth = 0;
		private int powerEast = 0;
		private int powerSouth = 0;
		private int powerWest = 0;
		private Boolean camp;
		private HashSet<EmptyBlock> eyes = new HashSet<EmptyBlock>();
		private HashSet<ISpot> spots = new HashSet<ISpot>();
		private int power;
		private int live = ALIVE;
		private int eyeCount = 0;

		public static final int ALIVE = 0;
		public static final int DYING = 1;
		public static final int DEAD = 2;
		private static final int MIN_DOUBLE_EYES = 7;
	}

	public static class EmptyBlock {

		public EmptyBlock() {
		}

		public EmptyBlock(ISpot spot) {
			this.addSpot(spot);
		}

		public void addSpot(ISpot spot) {
			spots.add(spot);
			spot.setEmptyBlock(this);
		}

		public void addEmptyBlock(EmptyBlock block) {
			HashSet<ISpot> blockspots = block.getSpots();
			if (blockspots == spots)
			{
				return;
			}
			spots.addAll(blockspots);

			for (ISpot spot : spots) {
				spot.setEmptyBlock(this);
			}
			if (block.over != null)
				this.over = block.over;
			this.influence |= block.influence;
		}

		public HashSet<ISpot> getSpots() {
			return spots;
		}

		public void addOver(Block block) {
			this.over.add(block);
		}

		public HashSet<Block> getOver() {
			return over;
		}

		public int getSize() {
			return spots.size();
		}

		public boolean hasBlack() {
			if (influence % 2 == 1)
				return true;
			return false;
		}

		public boolean hasWhite() {
			if (influence > 1)
				return true;
			return false;
		}

		public boolean hasOnlyBlack() {
			if (influence == Influencable.BLACK_INFLUENCE)
				return true;
			return false;
		}

		public boolean hasOnlyWhite() {
			if (influence == Influencable.WHITE_INFLUENCE)
				return true;
			return false;
		}

		public void setBlack() {
			if (influence % 2 == 0)
				influence++;
		}

		public void setWhite() {
			if (influence < 2)
				influence += 2;
		}

		public boolean hasAll() {
			if (influence == Influencable.COMMEN)
				return true;
			return false;
		}

		public boolean isFree() {
			if (influence == Influencable.FREE)
				return true;
			return false;
		}

		public void addInBlock(Block block) {
			in.add(block);
		}

		public int getInSize() {
			return in.size();
		}

		public HashSet<Block> getInBlock() {
			return in;
		}

		private HashSet<ISpot> spots = new HashSet<ISpot>();
		private HashSet<Block> in = new HashSet<Block>();
		private HashSet<Block> over = new HashSet<Block>();
		private int influence = Influencable.FREE;
	}
}
